package com.cjkj.applet.service.impl;

import com.cjkj.applet.domain.*;
import com.cjkj.applet.domain.dto.HospitalEquipmentDto;
import com.cjkj.applet.domain.dto.PatientRequest;
import com.cjkj.applet.domain.vo.PatientVo;
import com.cjkj.applet.mapper.*;
import com.cjkj.applet.service.ITPatVisitService;
import com.cjkj.common.core.constant.EquipmentConstants;
import com.cjkj.common.core.constant.PatVisitStatusConstants;
import com.cjkj.common.core.constant.SecurityConstants;
import com.cjkj.common.core.domain.runset.*;
import com.cjkj.common.core.exception.ServiceException;
import com.cjkj.common.core.utils.DateUtils;
import com.cjkj.common.core.utils.RunModeSetUtil;
import com.cjkj.common.core.utils.StringUtils;
import com.cjkj.common.core.utils.bean.BeanUtils;
import com.cjkj.common.core.utils.uuid.SnowflakeIdGenerator;
import com.cjkj.common.core.web.domain.AjaxResult;
import com.cjkj.common.security.utils.SecurityUtils;
import com.cjkj.system.api.RemoteTJ03Service;
import com.cjkj.system.api.domain.THospitalUser;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.Date;
import java.util.List;

/**
 * 患者就诊记录Service业务层处理
 * 
 * @author cjkj
 */
@Service
public class TPatVisitServiceImpl implements ITPatVisitService 
{
    @Resource
    private TPatIndexMapper patIndexMapper;

    @Resource
    private TPatVisitMapper tPatVisitMapper;

    @Resource
    private THospitalUserMapper hospitalUserMapper;

    @Resource
    private THospitalEquipmentMapper hospitalEquipmentMapper;

    @Resource
    private TEquipmentUsageRecordsMapper equipmentUsageRecordsMapper;

    @Resource
    private AppletAccountMapper appletAccountMapper;

    @Resource
    private AppletBindInfoMapper appletBindInfoMapper;

    @Resource
    private THospitalUserSignMapper hospitalUserSignMapper;

    @Resource
    private THospitalInfoMapper tHospitalInfoMapper;

    @Resource
    private TPatVisitSetMapper tPatVisitSetMapper;

    @Resource
    private RemoteTJ03Service remoteTJ03Service;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String add(PatientRequest req) {
        // 当前账号ID
        Long appUserId = SecurityUtils.getAppUserId();
        // 设备编号
        String equipmentNo = req.getEquipmentNo();
        // 数据类型
        String dataType = req.getDataType();

        // 判断医生账号是否存在
        String doctorUser = req.getUserName();
        THospitalUser hospitalUser = hospitalUserMapper.selectTHospitalUserByUserName(doctorUser);
        if(hospitalUser == null){
            throw new ServiceException("医生账号不存在！");
        }

        // 判断医生密码
        AppletAccount appletAccount = appletAccountMapper.selectAppletAccountByAccountId(appUserId);
        if(appletAccount == null || StringUtils.isBlank(appletAccount.getDoctor())) {
            // 还未绑定医生账号，开单时需要验证医生密码
            String userPassword = req.getUserPassword();
            if(StringUtils.isBlank(userPassword)) {
                throw new ServiceException("请输入医生密码！");
            }
            if(!SecurityUtils.matchesPassword(userPassword, hospitalUser.getPassword())) {
                throw new ServiceException("医生密码错误！");
            }
        }

        String hospitalId = hospitalUser.getHospitalId();
        String deptId = hospitalUser.getDeptId();
        String userId = hospitalUser.getUserId();

        req.setHospitalId(hospitalId);
        req.setDeptId(deptId);
        req.setCreateTime(DateUtils.getNowDate());

        // 判断设备是否存在
        HospitalEquipmentDto query = new HospitalEquipmentDto();
        query.setHospitalId(hospitalId);
        query.setDeptId(deptId);
        query.setEquipmentNo(equipmentNo);
        query.setEquipmentType(dataType);
        THospitalEquipment hospitalEquipment = hospitalEquipmentMapper.select(query);
        if(hospitalEquipment == null){
            throw new ServiceException("设备不存在！");
        }
        if(EquipmentConstants.H_EQUIPMENT_USE_FLAG_INUSE.equals(hospitalEquipment.getUseFlag())) {
            throw new ServiceException("当前设备正在使用中！");
        }

        // 校验设备使用剩余次数（睡眠监测垫需要）
        if(EquipmentConstants.EQUIPMENT_TYPE_MONITOR_PAD_TYPE.equals(dataType)) {
            // 分配次数
            Integer usageFrequency = hospitalEquipment.getUsageFrequency();
            // 已使用次数
            Integer useTimes = hospitalEquipment.getUseTimes();
            // 剩余次数
            int sycs = usageFrequency - useTimes;
            if (sycs <= 0 || sycs < req.getRentalDays()) {
                throw new ServiceException("设备"+equipmentNo+"剩余使用次数不足，请更换其他设备！");
            }
        }

        // 判断床位号是否占用
        bedIsOccupy(req);

        // 1、将患者基本信息存入t_pat_index表中
        Long patientId = saveTPatIndex(req);

        // 2、将患者本次就诊的信息写入t_pat_visit表中
        TPatVisit visitTemp = tPatVisitMapper.selectByPatientIdAndHospitalId(patientId, req.getHospitalId(), PatVisitStatusConstants.VISIT_STATUS_UNDERWAY, dataType);
        if (visitTemp != null) {
            throw new ServiceException("该患者有未结束的就诊");
        }

        TPatVisit visit = new TPatVisit();
        BeanUtils.copyBeanProp(visit, req);
        visit.setVisitNo(SnowflakeIdGenerator.getInstance().nextId());
        visit.setPatientId(patientId);
        visit.setHospitalId(hospitalId);
        visit.setDeptId(deptId);
        visit.setDoctorId(userId);
        visit.setVisitTime(req.getCreateTime());
        visit.setVisitStatus(PatVisitStatusConstants.VISIT_STATUS_UNDERWAY);
        visit.setAppletAccountId(appUserId);
        visit.setCreateBy(doctorUser);
        visit.setUpdateBy(doctorUser);
        visit.setUpdateTime(req.getCreateTime());

        // 查询该医院设置的绑定时长
        if(EquipmentConstants.EQUIPMENT_TYPE_SMART_BRACELET_TYPE.equals(dataType)) {
            THospitalInfo tHospitalInfo = tHospitalInfoMapper.selectTHospitalInfoByHospitalId(hospitalId);
            visit.setRentalDays(tHospitalInfo == null ? 24 : tHospitalInfo.getBindDuration());
        }

        // 心电血压监测仪需要保存运行设置项目
        if(EquipmentConstants.EQUIPMENT_TYPE_EB_TYPE.equals(dataType)) {
            req.setVisitNo(visit.getVisitNo());
            // 保存设置
            RunSetParam runSetParam = saveModeSet(req);
            // 获取最晚结束的时间作为整个就诊数据的预计结束时间
            if(runSetParam != null) {
                Long theLatestTime = RunModeSetUtil.getTheLatestTime(runSetParam);
                visit.setOutTime(new Date(theLatestTime));
            }
        }

        int visitFlag = tPatVisitMapper.insertTPatVisit(visit);
        if (visitFlag < 1) {
            throw new ServiceException("登记就诊信息出错");
        }

        // 3、更新设备使用记录
        req.setVisitNo(visit.getVisitNo());
        createEquipmentUseRecord(req, hospitalEquipment.getEquipmentId());

        // 4、更新医院设备使用状态
        updateHospitalEquipmentStatus(hospitalEquipment.getEquipmentId(), req.getUserName(), req.getCreateTime());

        /* 5.修改微信账户中绑定的医生账号 */
        AppletAccount account = new AppletAccount();
        account.setAccountId(appUserId);
        account.setDoctor(doctorUser);
        int i = appletAccountMapper.updateAppletAccount(account);
        if (i < 1) {
            throw new ServiceException("开单出错！");
        }

        /* 6.判断当前微信用户是否绑定了此设备 */
        AppletBindInfo bindInfo = appletBindInfoMapper.select(equipmentNo, appUserId);
        if(bindInfo == null) {
            bindInfo = new AppletBindInfo(null, appUserId, dataType, equipmentNo, req.getCreateTime(), null, "0");
            appletBindInfoMapper.insertAppletBindInfo(bindInfo);
        }

        /* 7.保存医生签名 */
        String signImg = req.getSignImg();
        if(StringUtils.isNotBlank(signImg)) {
            THospitalUserSign tHospitalUserSign = hospitalUserSignMapper.select(userId, signImg);
            if(tHospitalUserSign == null) {
                THospitalUserSign sign = new THospitalUserSign(userId, signImg);
                sign.setCreateTime(req.getCreateTime());
                hospitalUserSignMapper.insertSign(sign);
            }
        }

        return doctorUser;
    }
    /**
     * 判断床位是否占用
     */
    private void bedIsOccupy (PatientRequest req) {
        String bedNo = req.getBedNo();
        if (PatVisitStatusConstants.VISIT_LOCATION_HOSPITAL.equals(req.getPlace()) && StringUtils.isNotBlank(bedNo)) {
            TPatVisit param = new TPatVisit();
            param.setHospitalId(req.getHospitalId());
            param.setDeptId(req.getDeptId());
            param.setDataType(req.getDataType());
            param.setBedNo(bedNo);
            param.setVisitStatus(PatVisitStatusConstants.VISIT_STATUS_UNDERWAY);
            List<TPatVisit> tPatVisits = tPatVisitMapper.selectList(param);
            if(! tPatVisits.isEmpty()) {
                throw new ServiceException("当前床位已被占用");
            }
        }
    }

    private RunSetParam saveModeSet(PatientRequest req){
        // 1-动态血压模式，2-动态心电模式，3-动态血压+动态心电模式，4-普通血压计模式
        String mode = req.getMode();
        if(StringUtils.isBlank(mode) && "4".equals(mode)) {
            return null;
        }
        Long visitNo = req.getVisitNo();

        TPatVisitSet visitSet = new TPatVisitSet();
        visitSet.setVisitNo(visitNo);
        if("2".equals(mode) || "3".equals(mode)) {
            // 动态心电
            TimeSetDto ecgTime = req.getEcgTime();
            visitSet.setEcgTimeStart(ecgTime.getStart());
            visitSet.setEcgTimeEnd(ecgTime.getEnd());
        }

        if("1".equals(mode) || "3".equals(mode)) {
            TimeSetDto bpPlanDaytime = req.getBpPlanDaytime();
            TimeSetDto bpPlanNight = req.getBpPlanNight();

            visitSet.setBpPlan(req.getBpPlan());
            visitSet.setBpDayStartTime(bpPlanDaytime.getStart());
            visitSet.setBpDayEndTime(bpPlanDaytime.getEnd());
            visitSet.setBpDayReminderFlag(bpPlanDaytime.getReminder());
            visitSet.setBpDayIntervalTimes(bpPlanDaytime.getInterval());

            visitSet.setBpNightStartTime(bpPlanNight.getStart());
            visitSet.setBpNightEndTime(bpPlanNight.getEnd());
            visitSet.setBpNightReminderFlag(bpPlanNight.getReminder());
            visitSet.setBpNightIntervalTimes(bpPlanNight.getInterval());
        }
        // 先删除旧配置
        tPatVisitSetMapper.deleteTPatVisitSetByVisitNo(visitNo);
        // 保存设置
        tPatVisitSetMapper.insertTPatVisitSet(visitSet);

        // 发送命令
        SendCommandReq commandReq = new SendCommandReq();
        BeanUtils.copyBeanProp(commandReq, req);
        commandReq.setVisitTime(req.getCreateTime());
        RunSetParam setInfo = RunModeSetUtil.getSetInfo(commandReq);
        remoteTJ03Service.sendCommand(req.getEquipmentNo(), setInfo, SecurityConstants.INNER);

        return setInfo;
    }

    /**
     * 保存患者信息，并返回患者ID
     * @param req 患者信息
     * @return 患者ID
     */
    Long saveTPatIndex(PatientRequest req) {

        String hospitalId = req.getHospitalId();

        TPatIndex patIndex = new TPatIndex();
        patIndex.setPatientName(req.getPatientName());
        patIndex.setIdentityNumber(req.getIdentityNumber());
        patIndex.setGender(req.getGender());
        patIndex.setBirthday(req.getBirthday());
        patIndex.setPhone(req.getPhone());
        patIndex.setHospitalId(hospitalId);

        TPatIndex indexTemp = patIndexMapper.selectByIdNoAndHospitalId(patIndex.getIdentityNumber(), hospitalId);
        int indexFlag = 0;
        if (indexTemp == null) {
            patIndex.setPatientId(SnowflakeIdGenerator.getInstance().nextId());
            patIndex.setCreateBy(req.getUserName());
            patIndex.setCreateTime(req.getCreateTime());
            patIndex.setUpdateBy(req.getUserName());
            patIndex.setUpdateTime(req.getCreateTime());
            indexFlag = patIndexMapper.insertTPatIndex(patIndex);
        } else {
            patIndex.setUpdateBy(req.getUserName());
            patIndex.setUpdateTime(req.getCreateTime());
            patIndex.setPatientId(indexTemp.getPatientId());
            indexFlag = patIndexMapper.updateTPatIndex(patIndex);
        }
        if (indexFlag < 1) {
            throw new ServiceException("登记患者信息出错");
        }

        return patIndex.getPatientId();
    }

    /**
     * 创建设备使用记录
     */
    void createEquipmentUseRecord(PatientRequest req, String equipmentId) {
        TEquipmentUsageRecords record = new TEquipmentUsageRecords();
        record.setVisitNo(req.getVisitNo());
        record.setEquipmentId(equipmentId);
        record.setEquipmentType(req.getDataType());
        record.setEquipmentNo(req.getEquipmentNo());
        record.setUseFlag(EquipmentConstants.EQUIPMENT_USE_FLAG_INUSE);
        record.setStartTime(req.getCreateTime());
        record.setHospitalId(req.getHospitalId());
        record.setDeptId(req.getDeptId());
        record.setPlace(req.getPlace());
        record.setCreateBy(req.getUserName());
        record.setCreateTime(req.getCreateTime());
        record.setUpdateBy(req.getUserName());
        record.setUpdateTime(req.getCreateTime());
        int i = equipmentUsageRecordsMapper.insertTEquipmentUsageRecords(record);
        if (i < 1) {
            throw new ServiceException("分配设备出错！");
        }
    }

    /**
     * 更新医院设备使用情况
     */
    void updateHospitalEquipmentStatus(String equipmentId, String opName, Date opTime) {
        THospitalEquipment equipment = new THospitalEquipment();
        equipment.setEquipmentId(equipmentId);
        equipment.setUseFlag(EquipmentConstants.H_EQUIPMENT_USE_FLAG_INUSE);
        equipment.setUpdateBy(opName);
        equipment.setUpdateTime(opTime);
        int flag = hospitalEquipmentMapper.updateEquipmentUseStatus(equipment);
        if (flag < 1) {
            throw new ServiceException("更新设备使用状态失败");
        }
    }

    @Override
    public List<PatientVo> getList(String dataType) {
        // 当前账号ID
        String appOpenId = SecurityUtils.getAppOpenId();

        // 1、查询小程序账户信息
        AppletAccount account = appletAccountMapper.selectAppletAccountByOpenId(appOpenId);
        if(account == null) {
            throw new ServiceException("账户不存在！");
        }
        String doctor = account.getDoctor();
        if(StringUtils.isBlank(doctor)) {
            // 如果没有绑定医生账号，直接返回
            return Collections.emptyList();
        }

        // 2、查询医生信息
        THospitalUser hospitalUser = hospitalUserMapper.selectTHospitalUserByUserName(doctor);
        if(hospitalUser == null) {
            throw new ServiceException("医生账号不存在！");
        }

        return tPatVisitMapper.selectByDoctorId(hospitalUser.getUserId(), dataType);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult leaveHospital(List<Long> visitNos) {
        // 当前时间
        Date nowDate = DateUtils.getNowDate();
        // 医生账号
        AppletAccount appletAccount = appletAccountMapper.selectAppletAccountByAccountId(SecurityUtils.getAppUserId());
        String opName = appletAccount.getDoctor();
        for (Long visitNo: visitNos) {
            // 就诊状态标记为结束，更新出院时间
            TPatVisit patVisit = updateVisitStauts(visitNo, nowDate, opName);
            // 设备使用记录表标记为未使用
            String equipmentId = updateEquipmentUseRecord(visitNo, nowDate, opName);
            // 更新医院设备使用状态，将设备状态标记为空闲
            updateHospitalEquipmentStatus(equipmentId, patVisit.getVisitTime(), patVisit.getRentalDays(), nowDate, opName);
        }
        return AjaxResult.success();
    }

    private TPatVisit updateVisitStauts(Long visitNo, Date nowDate, String opName) {
        TPatVisit patVisit = tPatVisitMapper.selectTPatVisitByVisitNo(visitNo);
        if (patVisit == null) {
            throw new ServiceException("患者出院，未查询到患者信息");
        }

        patVisit.setVisitStatus(PatVisitStatusConstants.VISIT_STATUS_END);
        patVisit.setOutTime(nowDate);
        patVisit.setUpdateBy(opName);
        patVisit.setUpdateTime(nowDate);
        int count = tPatVisitMapper.updateTPatVisitStatus(patVisit);
        if (count < 1) {
            throw new ServiceException("患者出院，更新就诊信息出错！");
        }
        return patVisit;
    }

    private String updateEquipmentUseRecord(Long visitNo, Date nowDate, String opName){
        TEquipmentUsageRecords tEquipmentUsageRecords = equipmentUsageRecordsMapper.selectByVisitNo(visitNo, EquipmentConstants.EQUIPMENT_USE_FLAG_INUSE);
        if (tEquipmentUsageRecords == null) {
            throw new ServiceException("未找到关联的设备使用记录！");
        }
        tEquipmentUsageRecords.setUseFlag(EquipmentConstants.EQUIPMENT_RECORD_STATUS_END);
        tEquipmentUsageRecords.setEndTime(nowDate);
        tEquipmentUsageRecords.setUpdateBy(opName);
        tEquipmentUsageRecords.setUpdateTime(nowDate);
        int i = equipmentUsageRecordsMapper.updateTEquipmentUsageRecords(tEquipmentUsageRecords);
        if (i < 1) {
            throw new ServiceException("修改设备使用记录失败！");
        }

        return tEquipmentUsageRecords.getEquipmentId();
    }

    private void updateHospitalEquipmentStatus(String equipmentId, Date visitTime, Integer rentalDays, Date nowDate, String opName){
        THospitalEquipment tHospitalEquipment = hospitalEquipmentMapper.selectByEquipmentId(SecurityUtils.getAppUserId(), equipmentId);
        if (tHospitalEquipment == null) {
            throw new ServiceException("未找到关联的设备信息！");
        }
        tHospitalEquipment.setUseFlag(EquipmentConstants.H_EQUIPMENT_USE_FLAG_FREE);
        tHospitalEquipment.setUpdateBy(opName);
        tHospitalEquipment.setUpdateTime(nowDate);

        // 修改设备使用次数
        // 如果出院时间距离开单时间大于一小时，才减次数
        if(nowDate.getTime() - visitTime.getTime() > (60 * 60 * 1000)) {
            int days = 0;
            // 获取开单时间到当前出院时间的所有区间日期
            List<Date> allDatesInTheDateRange = DateUtils.getAllDatesInTheDateRange(visitTime, nowDate);
            for (int i = 0; i < allDatesInTheDateRange.size() - 1; i ++) {
                Date start = allDatesInTheDateRange.get(i);
                Date end = allDatesInTheDateRange.get(i + 1);
                long l = end.getTime() - start.getTime();
                if(l > (60 * 60 * 1000)) {
                    days += 1;
                }
            }
            // 实际租用天数
            rentalDays = Math.min(rentalDays, days);

            Integer oldTimes = tHospitalEquipment.getUseTimes();
            tHospitalEquipment.setUseTimes((oldTimes == null ? 0 : oldTimes) + rentalDays);
        }
        int i = hospitalEquipmentMapper.updateTHospitalEquipment(tHospitalEquipment);
        if (i < 1) {
            throw new ServiceException("修改设备状态失败！");
        }
    }

    /**
     * 查询患者就诊记录
     * 
     * @param visitNo 患者就诊记录主键
     * @return 患者就诊记录
     */
    @Override
    public TPatVisit selectTPatVisitByVisitNo(Long visitNo)
    {
        return tPatVisitMapper.selectTPatVisitByVisitNo(visitNo);
    }

    /**
     * 查询患者就诊记录列表
     * 
     * @param tPatVisit 患者就诊记录
     * @return 患者就诊记录
     */
    @Override
    public List<TPatVisit> selectTPatVisitList(TPatVisit tPatVisit)
    {
        return tPatVisitMapper.selectTPatVisitList(tPatVisit);
    }

    /**
     * 新增患者就诊记录
     * 
     * @param tPatVisit 患者就诊记录
     * @return 结果
     */
    @Override
    public int insertTPatVisit(TPatVisit tPatVisit)
    {
        tPatVisit.setCreateTime(DateUtils.getNowDate());
        return tPatVisitMapper.insertTPatVisit(tPatVisit);
    }

    /**
     * 修改患者就诊记录
     * 
     * @param tPatVisit 患者就诊记录
     * @return 结果
     */
    @Override
    public int updateTPatVisit(TPatVisit tPatVisit)
    {
        tPatVisit.setUpdateTime(DateUtils.getNowDate());
        return tPatVisitMapper.updateTPatVisit(tPatVisit);
    }

    /**
     * 批量删除患者就诊记录
     * 
     * @param visitNos 需要删除的患者就诊记录主键
     * @return 结果
     */
    @Override
    public int deleteTPatVisitByVisitNos(Long[] visitNos)
    {
        return tPatVisitMapper.deleteTPatVisitByVisitNos(visitNos);
    }

    /**
     * 删除患者就诊记录信息
     * 
     * @param visitNo 患者就诊记录主键
     * @return 结果
     */
    @Override
    public int deleteTPatVisitByVisitNo(Long visitNo)
    {
        return tPatVisitMapper.deleteTPatVisitByVisitNo(visitNo);
    }
}
